<script setup lang="ts">  
import { onBeforeMount, onMounted, ref, type Ref} from 'vue';
// 外层容器 | 内层容器
const wrapperEl:Ref<HTMLElement | null> = ref(null);  
const innerEl:Ref<HTMLElement | null> = ref(null);
// y轴滚动条 | x轴滚动条
const scrollBarYTarget = ref({
    // 滚动条
    el: null as HTMLElement | null,
    // 滚动条滑块
    thumbEl: null as HTMLElement | null,
    // 滚动条高度
    scrollSize: 0,
    // 上一次滚动位置
    prevScrollPos: 0,
    // 是否激活
    active: false,
});
const scrollBarXTarget = ref({
    el: null as HTMLElement | null,
    thumbEl: null as HTMLElement | null,
    scrollSize: 0,
    prevScrollPos: 0,
    active: false,
});
    
// resize监听器
let resizeObserver: ResizeObserver | null = null;
// 比例
const rates = ref({
    x: 0,
    y: 0
});
// 计算滚动条高度
const calcScrollBarData = (wrapperSize: number, innerSize: number, scrollSize: number) => {
    console.log(wrapperSize, innerSize, scrollSize);
    const calcRes = {rate: 0, scrollThumbSize: 0};
    if (wrapperSize < innerSize) {
        calcRes.rate = wrapperSize / innerSize;
        calcRes.scrollThumbSize = scrollSize * calcRes.rate;
    }
    return calcRes;
};
// 设置x轴滚动条
const setScrollBarX = () => {
    if(wrapperEl.value && innerEl.value && scrollBarXTarget.value.el && scrollBarXTarget.value.thumbEl){
        const {rate, scrollThumbSize} = calcScrollBarData(wrapperEl.value.clientWidth, innerEl.value.clientWidth, scrollBarXTarget.value.el.clientWidth);
        rates.value.x = rate;
        scrollBarXTarget.value.thumbEl.style.width = `${scrollThumbSize}px`;
    }
};
// 设置y轴滚动条
const setScrollBarY = () => {
    console.log(wrapperEl.value, innerEl.value, scrollBarYTarget.value.el, scrollBarYTarget.value.thumbEl);
    if(wrapperEl.value && innerEl.value && scrollBarYTarget.value.el && scrollBarYTarget.value.thumbEl){
        const {rate, scrollThumbSize} = calcScrollBarData(wrapperEl.value.clientHeight, innerEl.value.clientHeight, scrollBarYTarget.value.el.clientHeight);
        rates.value.y = rate;
        scrollBarYTarget.value.thumbEl.style.height = `${scrollThumbSize}px`;
    }
};
// 设置滚动条位置
const setWrapperScrollPos = (target: HTMLElement) => {
    if(target){
        if(scrollBarYTarget.value.thumbEl && rates.value.y && !scrollBarYTarget.value.active){
            const scrollSize = target.scrollTop * rates.value.y;
            scrollBarYTarget.value.thumbEl.style.transform = `translateY(${scrollSize}px)`;
            scrollBarYTarget.value.scrollSize = scrollSize;
        }
        if(scrollBarXTarget.value.thumbEl && rates.value.x && !scrollBarXTarget.value.active){
            const scrollSize = target.scrollLeft * rates.value.x;
            scrollBarXTarget.value.thumbEl.style.transform = `translateX(${scrollSize}px)`;
            scrollBarXTarget.value.scrollSize = scrollSize;
        }
    }
};
// 设置resize监听器
const setResizeObserver = () => {
    if(wrapperEl.value && innerEl.value){
        if(window.ResizeObserver){
            resizeObserver = new ResizeObserver((entrys) => {
                const elList = [wrapperEl.value, innerEl.value];
                entrys.forEach((entry) => {
                    if(elList.includes(entry.target as HTMLElement)){
                        setScrollBarX();
                        setScrollBarY();
                        setWrapperScrollPos(wrapperEl.value as HTMLElement);
                    }
                });
            });
            resizeObserver.observe(wrapperEl.value as HTMLElement);
            resizeObserver.observe(innerEl.value as HTMLElement);
        }else{
            window.addEventListener('resize', onResize);
        }
    }
};
// 移除resize监听器
const unSetResizeObserver = () => {
    if(resizeObserver){
        resizeObserver.unobserve(wrapperEl.value as HTMLElement);
        resizeObserver.unobserve(innerEl.value as HTMLElement);
    }else{
        window.removeEventListener('resize', onResize);
    }
};
// 滚动事件
const onWrapperScroll = (e: Event) => {
    setWrapperScrollPos(e.target as HTMLElement);
};
// resize事件
const onResize = () => {
    setScrollBarX();
    setScrollBarY();
    setWrapperScrollPos(wrapperEl.value as HTMLElement);
};
// 滚动条滑块鼠标按下事件 | 滚动条滑块鼠标移动事件 | 滚动条滑块鼠标抬起事件
const onScrollBarBarMouseDown = (e: MouseEvent) => {
    window.addEventListener('mousemove', onScrollBarBarMouseMove);
    window.addEventListener('mouseup', onScrollBarBarMouseUp);
    // 判断点击的是哪个滚动条
    if(e.target === scrollBarXTarget.value.thumbEl){
        scrollBarXTarget.value.prevScrollPos = e.clientX;
        scrollBarXTarget.value.active = true;
    }else if(e.target === scrollBarYTarget.value.thumbEl){
        scrollBarYTarget.value.prevScrollPos = e.clientY;
        scrollBarYTarget.value.active = true;
    }
};
const onScrollBarBarMouseMove = (e: MouseEvent) => {
    e.stopPropagation();
    e.preventDefault();
    // 判断是否为y轴滚动条
    if(scrollBarYTarget.value.active){
        const maxScrollSize = (scrollBarYTarget.value.el as HTMLElement).clientHeight - (scrollBarYTarget.value.thumbEl as HTMLElement).clientHeight;
        scrollBarYTarget.value.scrollSize += (e.clientY - scrollBarYTarget.value.prevScrollPos);
        if(scrollBarYTarget.value.scrollSize < 0){
            scrollBarYTarget.value.scrollSize = 0;
        }else if(scrollBarYTarget.value.scrollSize > maxScrollSize){
            scrollBarYTarget.value.scrollSize = maxScrollSize;
        }
        scrollBarYTarget.value.prevScrollPos = e.clientY;
        (scrollBarYTarget.value.thumbEl as HTMLElement).style.transform = `translateY(${scrollBarYTarget.value.scrollSize}px)`;
        wrapperEl.value?.scrollTo({
            top: scrollBarYTarget.value.scrollSize / rates.value.y,
            behavior: 'auto'
        });
    }
    // 判断是否为x轴滚动条
    if(scrollBarXTarget.value.active){
        const maxScrollSize = (scrollBarXTarget.value.el as HTMLElement).clientWidth - (scrollBarXTarget.value.thumbEl as HTMLElement).clientWidth;
        scrollBarXTarget.value.scrollSize += (e.clientX - scrollBarXTarget.value.prevScrollPos);
        if(scrollBarXTarget.value.scrollSize < 0){
            scrollBarXTarget.value.scrollSize = 0;
        }else if(scrollBarXTarget.value.scrollSize > maxScrollSize){
            scrollBarXTarget.value.scrollSize = maxScrollSize;
        }
        scrollBarXTarget.value.prevScrollPos = e.clientX;
        (scrollBarXTarget.value.thumbEl as HTMLElement).style.transform = `translateX(${scrollBarXTarget.value.scrollSize}px)`;
        wrapperEl.value?.scrollTo({
            left: scrollBarXTarget.value.scrollSize / rates.value.x,
            behavior: 'auto'
        });
    }
};
const onScrollBarBarMouseUp = () => {
    window.removeEventListener('mousemove', onScrollBarBarMouseMove);
    window.removeEventListener('mouseup', onScrollBarBarMouseUp);
    if(scrollBarXTarget.value.active){
        scrollBarXTarget.value.active = false;
        scrollBarXTarget.value.prevScrollPos = 0;
    }
    if(scrollBarYTarget.value.active){
        scrollBarYTarget.value.active = false;
        scrollBarYTarget.value.prevScrollPos = 0;
    }
};
onMounted(() => {
    // 初始化滚动条
    setScrollBarX();
    setScrollBarY();
    if(wrapperEl.value){
        setWrapperScrollPos(wrapperEl.value);
    }
    setResizeObserver();
});
onBeforeMount(() => {
    unSetResizeObserver();
});
</script>
<template>
	<div class="nd-scroll-container">
		<div
			ref="wrapperEl"
			class="nd-scroll-wrapper"
			@scroll="onWrapperScroll"
		>
			<div
				ref="innerEl"
				class="nd-scroll-inner"
			>
				<slot />
			</div>
		</div>
		<div
			:ref="el => {scrollBarXTarget.el = el as HTMLElement}"
			class="nd-scrollbar-x"
		>
			<div
				:ref="el => {scrollBarXTarget.thumbEl = el as HTMLElement}"
				class="nd-scrollbar-thumb"
				@mousedown="onScrollBarBarMouseDown"
			/>
		</div>
		<div
			:ref="el => {scrollBarYTarget.el = el as HTMLElement}"
			class="nd-scrollbar-y"
		>
			<div
				:ref="el => {scrollBarYTarget.thumbEl = el as HTMLElement}"
				class="nd-scrollbar-thumb"
				@mousedown="onScrollBarBarMouseDown"
			/>
		</div>
	</div>
</template>
<style lang="scss">
.nd-scroll-container{
    position: relative;
    .nd-scroll-wrapper{
        scrollbar-width: none;
        overflow: auto;
        z-index: 1;
        .nd-scroll-inner{
            width: fit-content;
            height: fit-content;
        }
    }
    .nd-scrollbar-x{
        position: absolute;
        bottom: 0;
        left: 0;
        width: 100%;
        z-index: 2;
        .nd-scrollbar-thumb{
            width: 0;
            height: 6px;
            border-radius: 6px;
            background-color: var(--nd-color-default);
            opacity: 0.3;
            &:hover{
                opacity: 0.4;
            }
        }
    }
    .nd-scrollbar-y{
        position: absolute;
        bottom: 0;
        right: 0;
        height: 100%;
        z-index: 2;
        .nd-scrollbar-thumb{
            width: 100%;
            border-radius: 6px;
            width: 6px;
            background-color: var(--nd-color-default);
            opacity: 0.3;
            &:hover{
                opacity: 0.4;
            }
        }
    }
}
</style>